Создаем админку для нашего модуля

В админке мы создадим отдельный блок. В этом блоке мы будем выводить стандартный список записей (Grid), форму работы с данными. Среди полей формы будет поле для выбора картинки и HTML редактор для форматированного вывода контента.

Для этого, нам нужно создать:

  1. класс контроллера модуля в админке (будет работать с данными реализовывать CRUD)
  2. класс блока для вывода grid-a
  3. класс хелпера - (у нас хелпер будет реализовывать функционал вывода полного пути картинки и url картинки)
  4. в файле конфигурации, нам нужно указать информацию о блоке и создать кнопку меню для перехода на наш блок
  5. Класс grid-a
  6. Класс блока для вывода формы
  7. Класс виджета формы

Создаем класс контроллера нашего модуля в админке

Контроллер будет заниматься выводом таблицы со списком записей и организовывать удаление, добавление (в том числе массовое удаление) и редактирование данных. Также, контроллер будет сохранять картинки, которые будет передаваться формой.

Создаем каталог:

app/code/local/My/Articles/controllers/Adminhtml

Создаем файл контроллера:

app/code/local/My/Articles/controllers/Adminhtml/ArticlesController.php

Добавляем код:

<?php

/**
* Class My_Articles_Adminhtml_ArticlesController
* Класс контроллера
*      1 выводит список записей
*      2 выводит форму для редактирования данных
*      3 обрабатывает данные из формы + сохраняет картинку
*      4 реализует массовое удаление данных
*/
class My_Articles_Adminhtml_ArticlesController extends Mage_Adminhtml_Controller_Action
{

   /**
    * Вывод таблицы со списком данных
    */
   public function indexAction()
   {
       /**
        * Загружаем Layout
        */
       $this->loadLayout();
       /**
        * Активируем меню
        */
       $this->_setActiveMenu('myarticles');
       /**
        * Получаем класс блока
        */
       $contentBlock = $this->getLayout()->createBlock('my_articles_block/adminhtml_articles');
       /**
        * Инициализируем контент
        */
       $this->_addContent($contentBlock);
       /**
        * Выводим данные
        */
       $this->renderLayout();
   }

   /**
    * Вывод формы для добавления данных
    * В данном случае перебрасывается на экшин редактирования
    */
   public function newAction()
   {
       $this->_forward('edit');
   }

   /**
    * Редактирование данных
    */
   public function editAction()
   {
       /**
        * Получаем id данных модели если нет - 0 (0 - это новая запись)
        */
       $id = (int) $this->getRequest()->getParam('id');
       /**
        * Получаем модель
        */
       $model = Mage::getModel('myarticles/articles')->load($id);
       /**
        * Регистрируем модель в глобальном секторе
        */
       Mage::register('current_article', $model);
       /**
        * Активируем меню
        */
       $this->loadLayout()->_setActiveMenu('myarticles');
       /**
        * Загружаем блок
        */
       $layout = $this->getLayout()->createBlock('my_articles_block/adminhtml_articles_edit');
       /**
        * Создаем контент
        */
       $this->_addContent($layout);
       /**
        * Выводим данные
        */
       $this->renderLayout();
   }

   /**
    * Сохранение данных из формы
    */
   public function saveAction()
   {
       /**
        * Получаем данные формы из POST
        */
       if ($data = $this->getRequest()->getPost()) {
           try {
               /**
                * Загружаем хелпер
                */
               $helper = Mage::helper('myarticles');
               /**
                * Загружаем модель
                */
               $model = Mage::getModel('myarticles/articles');
               /**
                * Передаем модели данные из POST
                */
               $model->setData($data)->setId($this->getRequest()->getParam('id'));
               /**
                * Если не указали дату - передаем текущую
                */
               if (!$model->getCreated()){
                   $model->setCreated(now());
               }
               /**
                * Сохраняем данные в модели
                */
               $model->save();
               /**
                * Получаем Id модели (если мы создаем новую запись - id мы узнаем после сохранения)
                */
               $id = $model->getId();
               /**
                * Смотрим, предала ли форма картинку
                * Из хелпера получаем путь для картинки и сохраняем ее
                * Если ошибка - удаляем картинку
                * Если все ок - записываем в модель имя картинки
                */
               if (isset($_FILES['image']['name']) && $_FILES['image']['name'] != '') {
                   $uploader = new Varien_File_Uploader('image');
                   $uploader->setAllowedExtensions(array('jpg', 'jpeg'));
                   $uploader->setAllowRenameFiles(false);
                   $uploader->setFilesDispersion(false);
                   $uploader->save($helper->getImagePath(), $id . '.jpg'); // Upload the image
                   $model->setImage($id . '.jpg');
                   $model->save();
               } else {
                   if (isset($data['image']['delete']) && $data['image']['delete'] == 1) {
                       @unlink($helper->getImagePath($id));
                   }
               }
               /**
                * Результат сохранения записываем в сессию и выводим сообщение
                */
               Mage::getSingleton('adminhtml/session')->addSuccess($this->__('Article was saved successfully'));
               Mage::getSingleton('adminhtml/session')->setFormData(false);
               $this->_redirect('*/*/');
           } catch (Exception $e) {
               Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
               Mage::getSingleton('adminhtml/session')->setFormData($data);
               $this->_redirect('*/*/edit', array(
                   'id' => $this->getRequest()->getParam('id')
               ));
           }
           return;
       }
       Mage::getSingleton('adminhtml/session')->addError($this->__('Unable to find item to save'));
       /**
        *  Перебрасываем на страницу со списком
        */
       $this->_redirect('*/*/');
   }

   /**
    * Удаление одной картинки
    */
   public function deleteAction()
   {
       /**
        *  Получаем id записи
        */
       if ($id = $this->getRequest()->getParam('id')) {
           try {
               /**
                * Удаляем запись
                */
               Mage::getModel('myarticles/articles')->setId($id)->delete();
               Mage::getSingleton('adminhtml/session')->addSuccess($this->__('Article was deleted successfully'));
           } catch (Exception $e) {
               Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
               $this->_redirect('*/*/edit', array('id' => $id));
           }
       }
       $this->_redirect('*/*/');
   }

   /**
    * Массовое удаление записей
    */
   public function massDeleteAction()
   {
       /**
        * Получаем список id записей
        */
       $articles = $this->getRequest()->getParam('article', null);
       /**
        * Если записи есть - обходим и по одной удаляем
        * После удаления - переходим в список записей и выводим в сообщении результат
        */
       if (is_array($articles) && sizeof($articles) > 0) {
           try {
               foreach ($articles as $id) {
                   Mage::getModel('myarticles/articles')->setId($id)->delete();
               }
               $this->_getSession()->addSuccess($this->__('Total of %d articles have been deleted', sizeof($articles)));
           } catch (Exception $e) {
               $this->_getSession()->addError($e->getMessage());
           }
       } else {
           $this->_getSession()->addError($this->__('Please select article'));
       }
       $this->_redirect('*/*');
   }
}

Создаем класс-хелпер

Нам понадобится хелпер для разных часто повторяющихся служебных случаев например, когда мы будем выводить наши данные на frontend-е - нам нужно будет определять наличие файлов картинок и наш хелпер нам поможет вернув полный путь к файлу по id записи. Также, наш хелпер поможет вернуть url картинки в общем, мы можем реализовать разные полезности в нем.

Создаем каталог:

app/code/local/My/Articles/Helper

Создаем файл:

app/code/local/My/Articles/Helper/Data.php

Добавляем код:

<?php

/**
 * Class My_Articles_Helper_Data
 * Класс хелпер
 * Реализует вывод полного пути по id
 * и вывод url по id записи
 */
class My_Articles_Helper_Data extends Mage_Core_Helper_Abstract
{

    /**
     * Функция реализует вывод полного пути к файлу картинки по id записи
     * Передаем id записи
     * @param int $id
     * @return string
     */
    public function getImagePath($id = 0)
    {
        $path = Mage::getBaseDir('media') . '/my_articles';
        if ($id) {
            return "{$path}/{$id}.jpg";
        } else {
            return $path;
        }
    }

    /**
     * Функция реализует вывод url к файлу картинки по id записи
     * Передаем id записи
     * @param int $id
     * @return string
     */
    public function getImageUrl($id = 0)
    {
        $url = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_MEDIA) . 'my_articles/';
        if ($id) {
            return $url . $id . '.jpg';
        } else {
            return $url;
        }
    }
}

Создаем класс блока в админке

Классы блоков используются Magento для вывода данных на странице. Так, мы передаем логику, которая необходима для вывода, классам блокам, а контроллерам передаем функционал обработки данных полученными из формы и сохранением их в базу.

Создаем каталоги:

app/code/local/My/Articles/Block
app/code/local/My/Articles/Block/Adminhtml

Создаем файл:

app/code/local/My/Articles/Block/Adminhtml/Articles.php

Добавляем код:

<?php

class My_Articles_Block_Adminhtml_Articles extends Mage_Adminhtml_Block_Widget_Grid_Container
{

    protected function _construct()
    {
        parent::_construct();
        $helper = Mage::helper('myarticles');
        $this->_blockGroup = 'my_articles_block';
        $this->_controller = 'adminhtml_articles';
        $this->_headerText = $helper->__('Article Management');
        $this->_addButtonLabel = $helper->__('Add Article');
    }
}

Добавляем параметры в файл конфигурации

В этом файле мы укажем Magento на наш модуль, добавим кнопку в меню и укажем информацию о нашем блоке.

app/code/local/My/Articles/etc/config.xml

Нам нужно добавить блоки (см. комментарии):

  1. Код для блока
  2. Код для админки
  3. Код для меню админки
<?xml version="1.0" ?>
<config>
   <modules>
       <My_Articles>
           <version>1.0.0</version>
       </My_Articles>
   </modules>
   <!-- Код для контроллера -->
   <frontend>
       <routers>
           <marticles>
               <use>standard</use>
               <args>
                   <module>My_Articles</module>
                   <frontName>articles</frontName>
               </args>
           </marticles>
       </routers>
   </frontend>
   <global>
       <!-- Код для моделей -->
       <models>
           <myarticles>
               <class>My_Articles_Model</class>
               <resourceModel>myarticles_recource</resourceModel>
           </myarticles>
           <!-- Код для ресурсов модели -->
           <myarticles_recource>
               <class>My_Articles_Model_Resource</class>
               <entities>
                   <table_myarticles>
                       <table>my_articles</table>
                   </table_myarticles>
               </entities>
           </myarticles_recource>
       </models>
       <!-- Код для добавления таблиц в базу -->
       <resources>
           <marticles_setup>
               <setup>
                   <module>My_Articles</module>
               </setup>
           </marticles_setup>
       </resources>
       <!-- Код для блока -->
       <blocks>
           <my_articles>
               <class>My_Articles_Block</class>
           </my_articles>
       </blocks>
       <!-- Код для хелпера -->
       <helpers>
           <myarticles>
               <class>My_Articles_Helper</class>
           </myarticles>
       </helpers>
   </global>
   <!-- Код для админки -->
   <admin>
       <routers>
           <myarticles_admin>
               <use>admin</use>
               <args>
                   <module>My_Articles</module>
                   <frontName>myarticles_admin</frontName>
               </args>
           </myarticles_admin>
       </routers>
   </admin>
   <!-- Код для меню админки -->
   <adminhtml>
       <menu>
           <myarticles module="myarticles">
               <title>My Articles</title>
               <sort_order>77</sort_order>
               <action>myarticles_admin/adminhtml_articles</action>
           </myarticles>
       </menu>
   </adminhtml>
</config>

Создаем Grid (вывод списка статей) в админке

Тут мы формируем вывод grid-a. Мы указываем какие поля мы будем выводить и формируем выпадающий список для действий с записями.

Создаем папку:

app/code/local/My/Articles/Block/Adminhtml/Articles

Создаем файл:

app/code/local/My/Articles/Block/Adminhtml/Articles/Grid.php

Добавляем код:

<?php

class My_Articles_Block_Adminhtml_Articles_Grid extends Mage_Adminhtml_Block_Widget_Grid
{

    /**
    Инициализируем коллекцию.
     */
    protected function _prepareCollection()
    {
        $collection  = Mage::getModel("myarticles/articles")->getCollection();
        $this->setCollection($collection);
        return parent::_prepareCollection();
    }

    /**
    Указываем поля, которые хотим видеть в гриде
     */
    protected function _prepareColumns()
    {
        $helper = Mage::helper('myarticles');
        $this->addColumn('id', array(
            'header' => $helper->__('Articles ID'),
            'width' => '50px',
            'index' => 'id'
        ));
        $this->addColumn('title', array(
            'header' => $helper->__('Title'),
            'index' => 'title',
            'type' => 'text',
        ));
        $this->addColumn('created', array(
            'header' => $helper->__('Created'),
            'index' => 'created',
            'type' => 'date',
        ));
        return parent::_prepareColumns();
    }

    /**
     * Возвращаем ссылку на форму редактирования данными
     * Кликнув на строку grid-а - мы перейдем на эту форму
     * @param $model
     * @return string
     */
    public function getRowUrl($model)
    {
        return $this->getUrl('*/*/edit', array(
            'id' => $model->getId(),
        ));
    }

    /**
     * В заголовке grid-a будет выводится выпадающий список для выбора действий
     * Тут мы добавляем в список параметр для удаления выбранных записей
     * @return $this
     */
    protected function _prepareMassaction()
    {
        $this->setMassactionIdField('id');
        $this->getMassactionBlock()->setFormFieldName('article');
        $this->getMassactionBlock()->addItem('delete', array(
            'label' => $this->__('Delete'),
            'url' => $this->getUrl('*/*/massDelete'),
        ));
        return $this;
    }
}

Создаем блок для редактирования

Тут мы создаем блок, внутри которого будет выводится виджет формы. Мы также инициализируем использование редактора (wysiwyg).

Создаем файл:

app/code/local/My/Articles/Block/Adminhtml/Articles/Edit.php

Добавляем код:

<?php

class My_Articles_Block_Adminhtml_Articles_Edit extends Mage_Adminhtml_Block_Widget_Form_Container
{

    protected function _prepareLayout()
    {
        parent::_prepareLayout();
        if (Mage::getSingleton('cms/wysiwyg_config')->isEnabled()) {
            $this->getLayout()->getBlock('head')->setCanLoadTinyMce(true);
        }
    }

    protected function _construct()
    {
        $this->_blockGroup = 'my_articles_block';
        $this->_controller = 'adminhtml_articles';
    }

    public function getHeaderText()
    {
        $helper = Mage::helper('myarticles');
        $model = Mage::registry('current_article');
        if ($model->getId()) {
            return $helper->__("Edit Articles item '%s'", $this->escapeHtml($model->getTitle()));
        } else {
            return $helper->__("Add Article item");
        }
    }
}

Создаем класс виджета формы

В этом классе, мы указываем поля, которые нам нужны в HTML форме.

Создаем каталог:

app/code/local/My/Articles/Block/Adminhtml/Articles/Edit

Создаем файл:

app/code/local/My/Articles/Block/Adminhtml/Articles/Edit/Form.php

Добавляем код:

<?php

class My_Articles_Block_Adminhtml_Articles_Edit_Form extends Mage_Adminhtml_Block_Widget_Form
{

    protected function _prepareForm()
    {
        $helper = Mage::helper('myarticles');
        $model = Mage::registry('current_article');
        $form = new Varien_Data_Form(array(
            'id' => 'edit_form',
            'action' => $this->getUrl('*/*/save', array(
                'id' => $this->getRequest()->getParam('id')
            )),
            'method' => 'post',
            'enctype' => 'multipart/form-data'
        ));
        $this->setForm($form);
        $fieldset = $form->addFieldset('myarticles_form', array('legend' => $helper->__('Article Information')));
        $fieldset->addField('title', 'text', array(
            'label' => $helper->__('Title'),
            'required' => true,
            'name' => 'title',
        ));
        $fieldset->addField('image', 'image', array(
            'label' => $helper->__('Image'),
            'name' => 'image',
        ));
        $fieldset->addField('header_h1', 'text', array(
            'label' => $helper->__('Header H1'),
            'required' => true,
            'name' => 'header_h1',
        ));
        $fieldset->addField('meta_tag_keywords', 'text', array(
            'label' => $helper->__('Keywords (SEO)'),
            'required' => true,
            'name' => 'meta_tag_keywords',
        ));
        $fieldset->addField('meta_tag_description', 'text', array(
            'label' => $helper->__('Description (SEO)'),
            'required' => true,
            'name' => 'meta_tag_description',
        ));
        $fieldset->addField('preview', 'editor', array(
            'label' => $helper->__('Preview'),
            'required' => true,
            'name' => 'preview',
        ));
        $fieldset->addField('content', 'editor', array(
            'label' => $helper->__('Content'),
            'wysiwyg' => true,
            'required' => true,
            'config' => Mage::getSingleton('cms/wysiwyg_config'),
            'name' => 'content',
        ));
        $fieldset->addField('created', 'date', array(
            'format' => Mage::app()->getLocale()->getDateFormat(Mage_Core_Model_Locale::FORMAT_TYPE_SHORT),
            'image' => $this->getSkinUrl('images/grid-cal.gif'),
            'label' => $helper->__('Created'),
            'name' => 'created'
        ));
        $form->setUseContainer(true);
        if($data = Mage::getSingleton('adminhtml/session')->getFormData()){
            $form->setValues($data);
        } else {
            $form->setValues($model->getData());
        }
        return parent::_prepareForm();
    }
}